/*
 * Global Utilities
 */

String.prototype.trim = function() {
	var str = this.replace(/^\s+/, '');
	for (var i = str.length - 1; i >= 0; i--) {
		if (/\S/.test(str.charAt(i))) {
			str = str.substring(0, i + 1);
			break;
		}
	}
	return str;
};

String.prototype.equals = function(str) {
	if (this == str)
		return true;
	return false;
};

Date.prototype.format = function(format) {
	var array = {
		"M+" : this.getMonth() + 1,
		"d+" : this.getDate(),
		"h+" : this.getHours(),
		"m+" : this.getMinutes(),
		"s+" : this.getSeconds(),
		"q+" : Math.floor((this.getMonth() + 3) / 3),
		"S" : this.getMilliseconds()
	}
	if (/(y+)/.test(format))
		format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4
						- RegExp.$1.length));
	for (var i in array)
		if (new RegExp("(" + i + ")").test(format))
			format = format.replace(RegExp.$1, RegExp.$1.length == 1
							? o[i]
							: ("00" + array[i]).substr(("" + array[i]).length));
	return format;
}

function $() {
	var elements = new Array();

	for (var i = 0; i < arguments.length; i++) {
		var element = arguments[i];
		if (typeof element == 'string')
			element = document.getElementById(element);

		if (arguments.length == 1)
			return element;

		elements.push(element);
	}

	return elements;
}

function bind(fn, self) {
	var boundargs = fn.boundArgs_ || [];
	boundargs = boundargs.concat(Array.prototype.slice.call(arguments, 2));
	if (typeof fn.boundSelf_ != "undefined") {
		self = fn.boundSelf_;
	}
	if (typeof fn.boundFn_ != "undefined") {
		fn = fn.boundFn_;
	}
	var newfn = function() {
		var args = boundargs.concat(Array.prototype.slice.call(arguments));
		return fn.apply(self, args);
	};
	newfn.boundArgs_ = boundargs;
	newfn.boundSelf_ = self;
	newfn.boundFn_ = fn;
	return newfn;
}

/*
 * Logging Utilities Level: OFF-0, ERROR-1, INFO-2, DEBUG-3
 */

function Logger(prefix) {
	this._prefix = prefix ? prefix : "";
	this._enabled = false;
	this._level = 3;
}

Logger.prototype = {

	trace : function() {
		if (this._level >= 3) {
			this._dumpMsg("DEBUG", "------Just Tracing------");
		}
	},

	debug : function(msg) {
		if (this._level >= 3) {
			this._dumpMsg("DEBUG", msg);
		}
	},

	info : function(msg) {
		if (this._level >= 2) {
			this._dumpMsg("INFO", msg);
		}
	},

	error : function(msg) {
		if (this._level >= 1) {
			this._dumpMsg("ERROR", msg);
		}
	},

	setLevel : function(level) {
		this._level = level;
	},

	_dumpMsg : function(strLevel, msg) {
		var date = new Date();
		var time = date.getFullYear() + "-" + date.getMonth() + "-"
				+ date.getDate() + " " + date.getHours() + ":"
				+ date.getMinutes() + ":" + date.getSeconds();
		var _msg = time + " [" + this._prefix + "]-[" + strLevel + "]: " + msg;
		Components.classes["@mozilla.org/consoleservice;1"]
				.getService(Components.interfaces.nsIConsoleService)
				.logStringMessage(_msg);
	}
};

/*
 * Locale Utilities
 */

var LocaleUtils = {

	getString : function(key) {
		var stringbundle = $("locale-string-bundle");
		return stringbundle.getString(key);
	},

	getFormattedString : function(key, strArray) {
		var stringbundle = $("locale-string-bundle");
		return stringbundle.getFormattedString(key, strArray);
	},

	encode : function(str) {
		return encodeURI(encodeURI(str));
	}
};

/*
 * URL Utilities
 */

var URLUtils = {
	isValidURL : function(url) {
		var RegExp = /^(([\w]+:)?\/\/)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,4}(:[\d]+)?(\/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?$/;
		if (RegExp.test(url)) {
			return true;
		} else {
			return false;
		}
	},

	transformURL : function(location, rawhref) {
		var href = rawhref.trim();
		if (this.isAbsoluteURL(href)) {
			return href;
		} else {
			var fixhref = location.protocol + "//" + location.hostname;
			if (location.port != 80 && location.port > 0) {
				fixhref += ":" + location.port;
			}
			if (href.charAt(0) == '/') {
				return fixhref + href;
			} else {
				var path = location.pathname;
				var idx = path.lastIndexOf('/');
				if (idx >= 0)
					path = path.substring(0, idx);
				return fixhref + path + "/" + href;
			}
		}
	},

	isAbsoluteURL : function(href) {
		if (href.length < 4)
			return false;
		var head = href.slice(0, 4);
		return head.toLowerCase() == "http";
	},

	removeQueryString : function(url) {
		if (url && url.indexOf('?') >= 0) {
			var pos = url.indexOf('?');
			return url.substring(0, pos);
		}
		return url;
	}
};

/*
 * Component Utilities
 */

var ComponentUtils = {
	_log : new Logger("ComponentUtils"),

	getTabBrowser : function() {
		var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
				.getService(Components.interfaces.nsIWindowMediator);
		var browserWindow = wm.getMostRecentWindow("navigator:browser");
		return browserWindow.getBrowser();
	},

	getAtomService : function(obj) {
		return Components.classes["@mozilla.org/atom-service;1"]
				.getService(Components.interfaces.nsIAtomService);
	},

	getPreferenceService : function() {
		return Components.classes["@mozilla.org/preferences-service;1"]
				.getService(Components.interfaces.nsIPrefBranch);
	},

	getPreference : function(key) {
		var service = this.getPreferenceService();
		return service.getCharPref(key);
	},

	setPreference : function(key, value) {
		var service = this.getPreferenceService();
		service.setCharPref(key, value);
	},

	getProxyService : function() {
		return Components.classes["@mozilla.org/network/protocol-proxy-service;1"]
				.getService()
				.QueryInterface(Components.interfaces.nsIProtocolProxyService);
	},

	getCacheService : function() {
		return Components.classes["@mozilla.org/network/cache-service;1"]
				.getService()
				.QueryInterface(Components.interfaces.nsICacheService);
	}
}

/*
 * Browser Utilities
 */

var BrowserUtils = {

	_log : new Logger("BrowserUtils"),

	getCurrentWindow : function() {
		return this.getSelectedBrowser().contentWindow;
	},

	getCurrentDoc : function() {
		return this.getSelectedBrowser().contentWindow.document;
	},

	getSelectedBrowser : function() {
		var tabbrowser = ComponentUtils.getTabBrowser();
		var browser = tabbrowser.selectedBrowser;
		return browser;
	},

	getFileSpec : function(file) {
		var nsIPH = Components.classes["@mozilla.org/network/protocol;1?name=file"]
				.createInstance(Components.interfaces.nsIFileProtocolHandler);
		return nsIPH.getURLSpecFromFile(file);
	},

	getPref : function(name, type, dfault, global) {
		var base = iForumExtension.getName() + ".";
		if (global)
			base = "";
		var prefs = Components.classes["@mozilla.org/preferences-service;1"]
				.getService(Components.interfaces.nsIPrefService)
				.getBranch(base);

		if (prefs.getPrefType(name) == prefs.PREF_INVALID)
			return dfault;
		try {
			switch (type) {
				case "str" :
					return prefs.getCharPref(name);
					break;
				case "int" :
					return prefs.getIntPref(name);
					break;
				case "bool" :
					return prefs.getBoolPref(name);
					break;
			}
			return null;
		} catch (e) {
			return dfault;
		}
	},

	setPref : function(name, type, value, global) {
		var base = iForumExtension.getName() + ".";
		if (global)
			base = "";
		var prefs = Components.classes["@mozilla.org/preferences-service;1"]
				.getService(Components.interfaces.nsIPrefService)
				.getBranch(base);

		switch (type) {
			case "str" :
				prefs.setCharPref(name, value);
				break;
			case "int" :
				prefs.setIntPref(name, value);
				break;
			case "bool" :
				prefs.setBoolPref(name, value);
				break;
		}
	},

	showInTab : function(url) {
		var tabbrowser = ComponentUtils.getTabBrowser();
		tabbrowser.selectedTab = tabbrowser.addTab("");
		var browser = tabbrowser.selectedBrowser;
		browser.loadURI(url);
	}
};

/*
 * DOM Utilities
 */

var DOMUtils = {

	removeChildren : function(node) {
		if (!node)
			return;
		while (node.hasChildNodes()) {
			node.removeChild(node.firstChild);
		}
	},

	getXPath : function(target) {
		var useUpperCase = true;
		var targetDoc = target.ownerDocument;

		// Get all the nodes in the path
		var nodePathes = [];
		while (target.nodeType == Node.ELEMENT_NODE
				|| target.nodeType == Node.TEXT_NODE) {
			nodePathes.unshift(target);
			if (target.nodeType == Node.ELEMENT_NODE
					&& target.hasAttribute("id")) {
				var id = target.getAttribute("id");
				if (targetDoc.getElementById(id) == target) {
					break;
				}
			}
			target = target.parentNode;
		}

		var nodeNames = [];
		var start = "/";
		for (var i in nodePathes) {
			var node = nodePathes[i];
			var nodeIndex = this.getNodeIndex(node);
			if (node.nodeType == Node.ELEMENT_NODE) {
				if (i == 0 && node.hasAttribute("id")) {
					nodeNames.push("id('" + node.getAttribute("id") + "')");
					start = "";
				} else {
					if (useUpperCase) {
						var tagName = node.tagName.toUpperCase();
					} else {
						var tagName = node.tagName;
					}
					nodeNames.push(tagName + "[" + nodeIndex + "]");
				}
			} else if (node.nodeType == Node.TEXT_NODE) {
				nodeNames.push("text()[" + nodeIndex + "]");
			}
		}
		return start + nodeNames.join("/");
	},

	getChildNodesByName : function(parent, tagName) {
		var nodes = [];
		var child = parent.firstChild;
		while (child != null) {
			if (child.tagName && child.tagName == tagName) {
				nodes.push(child);
			}
			child = child.nextSibling;
		}
		return nodes;
	},
	getNodeIndex : function(node) {
		var index = 1;
		var child = node.parentNode.firstChild;
		while (child != null) {
			if (child == node)
				return index;
			if (node.nodeType == child.nodeType) {
				if (node.nodeType == Node.ELEMENT_NODE) {
					var flag = child.localName.toUpperCase() == "SPAN"
							&& child.hasAttribute(GRUBBER_HIGHLIGHT_ID)
					if (!flag && child.tagName == node.tagName) {
						index++;
					}
				} else {
					index++;
				}
			}
			child = child.nextSibling;
		}
		throw "couldn't find node in parent's list: " + node.tagName;
	},

	getLinkNode : function(node) {
		while (node.nodeType == Node.ELEMENT_NODE
				|| node.nodeType == Node.TEXT_NODE) {
			if (node.nodeType == Node.ELEMENT_NODE) {
				var localName = node.localName;
				if (localName == "A" || localName == "a") {
					return node;
				}
				node = node.parentNode;
				if (node == null)
					return null;
			}
		}
		return null;
	},

	getPreorderNodeList : function(parent) {
		var nodes = new Array();
		nodes.push(parent);
		if (parent.hasChildNodes()) {
			var children = parent.childNodes;
			for (var i = 0; i < children.length; i++) {
				var child = children.item(i);
				var grandchildren = this.getPreorderNodeList(child);
				nodes = nodes.concat(grandchildren);
			};
		}
		return nodes;
	},

	selectNodes : function(ownerdocument, xpath) {
		var xpathEvaluator = new XPathEvaluator();
		var results = xpathEvaluator
				.evaluate(xpath, ownerdocument.documentElement, null,
						XPathResult.ANY_TYPE, null);
		var nodes = [];
		var item = results.iterateNext();
		while (item != null) {
			nodes.push(item);
			item = results.iterateNext();
		}
		return nodes;
	},

	getAncestorNode : function(ownerdocument, elmt, up) {
		var p = elmt;
		for (var index = 0; index < up; index++) {
			p = p.parentNode;
		}
		return p;
	},

	getTrimHTML : function(elmts) {
		var startOffset = 0, endOffset = elmts.length - 1;
		for (var i = 0; i < elmts.length; i++) {
			var elmt = elmts[i];
			if ((elmt.nodeType == Node.ELEMENT_NODE && elmt.nodeName == "BR")
					|| (elmt.nodeType == Node.TEXT_NODE && elmt.textContent
							.trim().length == 0)) {
				startOffset++;
			} else {
				break;
			}
		}
		for (var i = elmts.length - 1; i > 0; i--) {
			var elmt = elmts[i];
			if ((elmt.nodeType == Node.ELEMENT_NODE && elmt.nodeName == "BR")
					|| (elmt.nodeType == Node.TEXT_NODE && elmt.textContent
							.trim().length == 0)) {
				endOffset--;
			} else {
				break;
			}
		}

		var html = "";
		for (var i = startOffset; i <= endOffset; i++) {
			var elmt = elmts[i];

			if (elmt.nodeType == Node.TEXT_NODE) {
				html += elmt.textContent;

			} else if (elmt.nodeType == Node.ELEMENT_NODE) {
				html += "<" + elmt.nodeName;
				if (elmt.hasAttributes()) {
					var attrs = elmt.attributes;
					for (var j = 0; j < attrs.length; j++) {
						var attr = attrs[j];
						html += " " + attr.name + "=\"" + attr.value + "\"";
					}
				}

				html += ">" + elmt.innerHTML;
				html += "</" + elmt.nodeName + ">";
			}
		}

		return html;
	},

	getHeadElement : function(contentDocument) {
		var headElementList = contentDocument.getElementsByTagName("head");
		// If there is a head element
		if (headElementList.length > 0) {
			return headElementList[0];
		}
		return contentDocument.documentElement;
	},

	removeElement : function(element) {
		if (element && element.parentNode) {
			element.parentNode.removeChild(element);
		}
	},

	toggle : function(id, attr) {
		var node = $(id);
		if (node) {
			var value = node[attr];
			node[attr] = !value;
		}
	}
};